﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using PClusterModel.Infrastructure;

namespace PClusterModel.Cluster
{
    internal class MaximalDimensionSetFinder
    {
        protected PClusterizer clusterizer;
        protected int minNumberOfObjects;
        protected int minNumberOfConditions;
        protected byte trashold;

        public MaximalDimensionSetFinder(PClusterizer clusterizer)
        {
            this.clusterizer = clusterizer;
            this.minNumberOfConditions = this.clusterizer.MinNumberOfConditions;
            this.minNumberOfObjects = this.clusterizer.MinNumberOfObjects;
            this.trashold = this.clusterizer.Trashold;
        }

        public virtual void CalculateMaximalDimensionSetsAndPlaceIntoBlocks()
        {
            int objectCount = this.clusterizer.DataSet.NumberOfDataObjects;
            for (int i = 0; i < objectCount - 1; i++)
            {
                for (int j = i + 1; j < objectCount; j++)
                {
                    IMicroarrayDataObject firsObject = this.clusterizer.DataSet.MicroarrayDataObjects[i];
                    IMicroarrayDataObject secondObject = this.clusterizer.DataSet.MicroarrayDataObjects[j];

                    MaximalDimensionBlockSet maximalDimensionSet = FindMaximalDimensionSet(firsObject, secondObject);
                    if (maximalDimensionSet != null)
                    {
                        this.PlaceMaximalDimensionSetInBlock(maximalDimensionSet, firsObject);
                        this.PlaceMaximalDimensionSetInBlock(maximalDimensionSet, secondObject);
                    }
                }
            }
        }

        protected void PlaceMaximalDimensionSetInBlock(MaximalDimensionBlockSet maximalDimensionSet, IMicroarrayDataObject dataObject)
        {
            if (!this.clusterizer.Blocks.ContainsKey(dataObject))
            {
                this.clusterizer.Blocks.Add(dataObject, new Block(dataObject));
            }
            this.clusterizer.Blocks[dataObject].MaximalDimensionSets.Add(maximalDimensionSet);
        }

        protected virtual MaximalDimensionBlockSet FindMaximalDimensionSet(IMicroarrayDataObject firstObject, IMicroarrayDataObject secondObject)
        {
            List<KeyValuePair<int, float>> substractedValues = new List<KeyValuePair<int, float>>();
            int conditionCount = firstObject.ExpresionValues.Count;

            for (int i = 0; i < conditionCount; i++)
            {
                int firstValue = firstObject.ExpresionValues[i];
                int secondValue = secondObject.ExpresionValues[i];

                int difference = secondValue - firstValue;
                substractedValues.Add(new KeyValuePair<int, float>(i, difference));
            }

            substractedValues.Sort(new PClusterModel.Comparer.KeyValuePairComparerByValue<int, float>());
            IList<BitArray> maximalDimensionSetBitmaps = this.ExtractMaximalDimensionSetBitMaps(substractedValues);
            if (maximalDimensionSetBitmaps.Count > 0)
            {
                MaximalDimensionBlockSet maxDimensionSet = new MaximalDimensionBlockSet(firstObject, secondObject);
                maxDimensionSet.ConditionBitMaps.AddRange(maximalDimensionSetBitmaps);
                return maxDimensionSet;
            }
            return null;
        }

        protected IList<BitArray> ExtractMaximalDimensionSetBitMaps(IList<KeyValuePair<int, float>> sortedValues)
        {
            List<BitArray> bitMaps = new List<BitArray>();
            int leftIndex = 0, rightIndex = 0;
            float difference;
            int count = sortedValues.Count;
            bool newMds = false;
            KeyValuePair<int, float> currentRight, currentLeft;

            while (rightIndex < count)
            {
                currentRight = sortedValues[rightIndex];
                currentLeft = sortedValues[leftIndex];
                difference = this.CalculateDifference(currentRight.Value, currentLeft.Value);
                if (this.CheckDifference( difference)|| leftIndex == rightIndex)
                {
                    rightIndex++;
                    newMds = true;
                }
                else
                {
                    if (rightIndex - leftIndex >= minNumberOfConditions && newMds)
                    {
                        BitArray bitmap = ExtractBitMap(sortedValues, leftIndex, rightIndex);
                        bitMaps.Add(bitmap);
                    }
                    leftIndex++;
                    newMds = false;
                }
            }
            if (rightIndex - leftIndex >= minNumberOfConditions && newMds)
            {
                BitArray bitmap = this.ExtractBitMap(sortedValues, leftIndex, rightIndex);
                bitMaps.Add(bitmap);
            }
            return bitMaps;
        }

        protected virtual bool CheckDifference(float difference)
        {
            return difference <= this.trashold;
        }

        private BitArray ExtractBitMap(IList<KeyValuePair<int, float>> orderedValues, int leftIndex, int rightIndex)
        {
            int count = this.clusterizer.DataSet.NumberOfConditions;
            BitArray bitMap = new BitArray(count, false);
            KeyValuePair<int, float> current;
            for (int i = leftIndex; i < rightIndex; i++)
            {
                current = orderedValues[i];
                int containedColumn = current.Key;
                bitMap.Set(containedColumn, true);
            }
            return bitMap;
        }

        protected virtual float CalculateDifference(float first, float second)
        {
            return first - second;
        }
    }
}
